home *** CD-ROM | disk | FTP | other *** search
/ TPUG - Toronto PET Users Group / TPUG Users Group CD / TPUG Users Group CD.iso / AMIGA / (A)Z / (A)Z8.ADF / YaboingII / yb2.c < prev    next >
C/C++ Source or Header  |  1988-01-05  |  17KB  |  506 lines

  1. /* yb2.c (c)1988 Ali T. Ozer
  2. ** main() and tons of other stuff for YaBoing II.
  3. ** Freely distributable.
  4. */
  5.  
  6. /* This is a second version of YaBoing!, "Yet Another Boing." Actually, this
  7. ** program started off with "I should fix YaBoing! up so that it'll run on 
  8. ** morerows'ed or interlaced screens." But I got carried away --- This program
  9. ** has almost nothing to do with the original YaBoing! (except that the 
  10. ** goal involves chasing sprites aroung with the mouse pointer).
  11. **
  12. ** YaBoing! originally written Sep 1986 and posted Sep 21, 1986.
  13. ** YaBoing II written Dec 1987.
  14. */
  15.  
  16. /* YaBoing II is really a "stack-calculator" simulator where you have to 
  17. ** hunt down the numbers to be input and the operators to be applied. 
  18. **
  19. ** -The "calculator" has a 4-location stack. The window displays the four
  20. **  entries, with the bottom of the stack at the top (right under the title
  21. **  bar).
  22. ** -Inputs are one-digit only; when you catch a "number" sprite the value (0-9)
  23. **  is pushed to the stack. 
  24. ** -If the stack overflows, you lose the bottom entries. 
  25. ** -The operators "+", "-", "*", "/", pop the top two entries of the stack and
  26. **  push the result. If you push X and then Y, the result is X op Y 
  27. **  (and not Y op X). If the stack has less than 2 entries, nothing happens.
  28. ** -The "POP" operator pushes the top element off the stack.
  29. ** -The "SWAP" operator swaps the top two elements.
  30. ** -The "?" is a mystery number greater than 9.
  31. ** -The stack locations hold 32-bit unsigned numbers. If "+" or "*" causes 
  32. **  overflow, the result is simply truncated.
  33. ** -If subtraction causes a negative result, then the result is 0.
  34. ** -If you divide by ZERO, the system GURUs. 
  35. ** -No, no, kidding. If you divide by ZERO the stack is cleared.
  36. **
  37. ** So what's the goal? To end up with the highest number on top of the stack
  38. ** at the end of the game. The game lasts about 40-45 seconds, and about 
  39. ** five seconds before the end your mouse pointer changes shape to warn you.
  40. **
  41. ** The high score is (2^32) - 1. Good luck!
  42. **
  43. ** -Ali 
  44. */
  45.  
  46. /* Besides changes in the game-play, there are also some technical
  47. ** differences between YaBoing! and YaBoing II:
  48. **
  49. ** -Sprites are not bound to the WB screen --- when WB is pulled down, the 
  50. **  sprites will remain displayed. On some screens the sprites might get 
  51. **  splattered (depending on your preferences settings).
  52. ** -The valid sprite movement area is determined by looking at the user's
  53. **  screen parameters.
  54. ** -The Amiga timer device is used to time the sprite movement rather than
  55. **  just a counter. Thus sprites will move around at a somewhat constant rate
  56. **  (as opposed to slowing down when the load is high), although the movement
  57. **  might be rather jerky.
  58. ** -The game is deactivated when the window is made inactive (like in YaBoing!).
  59. **  to continue the game it's not enough to activate the window --- You need to
  60. **  click either mouse button anywhere within the window (not on a gadget).
  61. **  This allows you to depth-arrange and move the window without the sprites in
  62. **  the way.
  63. */
  64.  
  65.  
  66. /****************************************************************************/
  67.  
  68. #include "yb2.h"   
  69.  
  70. /* The following declarations replace Manx's and save about 900 bytes. Don't
  71. ** worry about the linker's "multiply defined" complaints...
  72. */
  73. _wb_parse () {}
  74. _cli_parse () {}
  75.  
  76. /* Collision bits in register CLXDAT. The three bits below correspond 
  77. ** to collisions between sprite 0 (the mouse) and sprites 2, 4, and 6,
  78. ** respectively.
  79. */
  80. #define COL0AND2 0x0200
  81. #define COL0AND4 0x0400
  82. #define COL0AND6 0x0800
  83. #define COL2AND4 0x1000
  84. #define ALLCOL   0x1e00
  85.  
  86. unsigned short colmasks[] = {COL0AND2, COL0AND4, COL0AND6};
  87.  
  88. #define MAXVEL 24   /* Twice max velocity in pixels/move */
  89.  
  90. /* Number of sprites. There are dependencies on the value of this, for
  91. ** instance, InitSprites asks for sprites by number, and if this number is 
  92. ** changed, the mapping in InitSprites for yaboing sprite num -> HW sprite
  93. ** should be fixed also.
  94. */
  95. #define NUMSPR 3
  96. struct sprrec ybspr[NUMSPR];
  97.  
  98. struct timerequest tr;  
  99. struct Screen     *scr;   /* WorkBench screen, obtained from the window */
  100. struct Window      *win;   /* YaBoing window */
  101. struct ViewPort      *vp;    /* WorkBench ViewPort */
  102. struct RastPort   *rp;    /* YaBoing window rastport */
  103. struct Font       *font;  /* Topaz 8 --- We need 8-point font for text */
  104. struct GfxBase    *GfxBase;
  105. struct IntuitionBase *IntuitionBase;
  106.  
  107. int minx, miny, maxx, maxy, halfx, halfy, quartx, quarty; /* Screen params */
  108. int xshiftfactor, yshiftfactor; /* For conversion from screen to LORES     */
  109. int mousex, mousey; /* Updated everytime through the loop, LORES-coords    */
  110. int spritecount;    /* Increments everytime a new sprite is generated.     */
  111. unsigned long lastmove; /* The time at which sprites last moved */
  112. long oldtaskpri = 0;    
  113. struct Task *me;        
  114.  
  115. #define MAXSPRITES 110 /* The number of sprites generated before game ends  */
  116. #define WARNSPRITE  94 /* The number of sprites after which warning's given */
  117.  
  118.  
  119. /* Returns a value that increments every 1/16 second... 
  120. */
  121. unsigned long TimeCount()
  122. {
  123.   DoIO (&tr);
  124.   return ((tr.tr_time.tv_secs << 4L) + (tr.tr_time.tv_micro / 62500L));
  125. }
  126.  
  127.  
  128. main ()
  129. {
  130.     register int cnt;
  131.     unsigned short clxdat;    /* Value of collision register */
  132.     int sleeping = true;      /* True if game is inactive */
  133.     struct IntuiMessage *msg; /* The intuition message, from our window port */
  134.  
  135.     /* First set the priority of this task. */
  136.     if (me = FindTask (NULL)) oldtaskpri = SetTaskPri (me, 1L); 
  137.  
  138.     OpenStuff ();
  139.     InitRnd ();  /* Start up the random number generator */
  140.     InitMessage ();
  141.  
  142.     while (1) {
  143.  
  144.     while (msg = (struct IntuiMessage *)GetMsg (win->UserPort)) {
  145.         switch (msg->Class) {
  146.               case CLOSEWINDOW:    
  147.                 ReplyMsg (msg); 
  148.         CloseStuff (0);  /* Never returns */
  149.           case MOUSEBUTTONS:   
  150.                 if (msg->Code==SELECTDOWN || msg->Code==MENUDOWN) 
  151.                   if (sleeping) {
  152.               sleeping = false;
  153.                       if (spritecount == 0) NewGame ();
  154.                       ShowSprites (true);
  155.                   } else sleeping = true;
  156.               default:             
  157.                 ReplyMsg (msg);
  158.             }
  159.         }
  160.  
  161.         if (spritecount > MAXSPRITES) {
  162.  
  163.       SetWarnPointer (win, false);
  164.           DisplayBeep (scr);
  165.           ShowScore ();
  166.       while (msg = (struct IntuiMessage *)GetMsg (win->UserPort))
  167.               ReplyMsg (msg);
  168.       sleeping = true;
  169.       spritecount = 0;
  170.         };
  171.  
  172.         if (sleeping) {
  173.  
  174.           ShowSprites (false);
  175.           Wait (1L << win->UserPort->mp_SigBit);
  176.  
  177.  
  178.         } else {                     
  179.  
  180.           mousex = LoResMouseX();
  181.           mousey = LoResMouseY();
  182.  
  183.       for (cnt = 0; cnt < NUMSPR; cnt++) ProcessSprite (&ybspr[cnt]);
  184.       lastmove = TimeCount ();
  185.           for (cnt = 0; cnt < NUMSPR; cnt++) LocateSprite (&ybspr[cnt]);
  186.       
  187.           if ((clxdat = custom.clxdat) & ALLCOL) CheckCollisions(clxdat);
  188.           else Delay (2L);   /* Don't hog the CPU too much! */
  189.  
  190.           WaitTOF ();
  191.         }
  192.    }
  193. }
  194.  
  195. /* Mouse coords in LORES (same kind of values as sprite locations) 
  196. */
  197. int LoResMouseX () { return ((scr->MouseX + vp->DxOffset) >> xshiftfactor); }
  198. int LoResMouseY () { return ((scr->MouseY + vp->DyOffset) >> yshiftfactor); }
  199.  
  200. /* Panic puts up a requester with a single "Sigh..." box. The string
  201. ** provided in "reason" is printed in the body of the  requester. 
  202. ** If user hits "Retry," then Panic returns. Else it exits.
  203. */
  204. Panic (reason)
  205. UBYTE *reason;
  206. {
  207.   static struct IntuiText negtxt  = {0,1,COMPLEMENT,4,4,NULL,(UBYTE *)"Sigh...",NULL};
  208.   static struct IntuiText bodytxt = {0,1,COMPLEMENT,10,6,NULL,NULL,NULL};
  209.  
  210.   bodytxt.IText = reason;
  211.   if (AutoRequest (NULL, &bodytxt, NULL, &negtxt, 0L, 0L, 300L, 54L)) return;
  212.   CloseStuff (5);
  213. }
  214.  
  215. CloseStuff (exitcode)
  216. int exitcode;
  217. {
  218.     register int cnt;
  219.  
  220.     for (cnt = 0; cnt < NUMSPR; cnt++) ReleaseSprite (&ybspr[cnt]);
  221.  
  222.     if (tr.tr_node.io_Message.mn_Node.ln_Type == NT_MESSAGE) CloseDevice (&tr);
  223.     if (font) CloseFont (font);
  224.     if (win) CloseWindow (win);
  225.     if (GfxBase) CloseLibrary (GfxBase);
  226.     if (IntuitionBase) CloseLibrary (IntuitionBase);
  227.     if (me) SetTaskPri (me, oldtaskpri); 
  228.     exit (exitcode);
  229. }
  230.  
  231. OpenStuff ()
  232. {
  233.     unsigned short sprcol;  /* Used when setting */
  234.     long creg;              /* sprite colors...  */
  235.  
  236.     static struct NewWindow ybwindow = {
  237.       WINDOWX, WINDOWY, WINDOWWIDTH, WINDOWHEIGHT, -1, -1,
  238.       CLOSEWINDOW | MOUSEBUTTONS, 
  239.       SMART_REFRESH | WINDOWCLOSE | WINDOWDEPTH | ACTIVATE |
  240.           WINDOWDRAG | NOCAREREFRESH | RMBTRAP,
  241.       NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, WBENCHSCREEN
  242.     };
  243.  
  244.     static struct TextAttr ybfontdesc = {(STRPTR)"topaz.font", 8, 0, 0};
  245.  
  246.     if (((IntuitionBase = (struct IntuitionBase *)
  247.            OpenLibrary ("intuition.library", 0L)) == NULL) ||
  248.         ((GfxBase = (struct GfxBase *)
  249.            OpenLibrary ("graphics.library", 0L)) == NULL) ||
  250.         ((font = OpenFont (&ybfontdesc)) == NULL)) CloseStuff (10); /* ROM? */
  251.  
  252.     if (OpenDevice(TIMERNAME, UNIT_VBLANK, &tr, 0L) != 0) Panic ("No timer");
  253.     tr.tr_node.io_Message.mn_Node.ln_Type = NT_MESSAGE;
  254.     tr.tr_node.io_Command = TR_GETSYSTIME;
  255.  
  256.     if (InitSprites () == false) Panic ("No sprites!");
  257.     if ((win = OpenWindow (&ybwindow)) == NULL) Panic ("No memory");
  258.  
  259.     /* Get the various stuff we want to access often into global variables. */
  260.     scr = win->WScreen;
  261.     vp  = (struct ViewPort *)ViewPortAddress (win);
  262.     rp  = win->RPort;
  263.  
  264.     sprcol = GetRGB4 (vp->ColorMap, 0L) + 0x0888;
  265.     
  266.     for (creg = 20L; creg < 32L; creg += 2L) {
  267.       SetRGB4 (vp, creg, 0L, 0L, 0L);
  268.       SetRGB4 (vp, creg+1, (long)((sprcol & 0x0f00) >> 8),
  269.                (long)((sprcol & 0x00f0) >> 4),
  270.                (long)(sprcol & 0x000f));
  271.     }
  272.  
  273.     SetAPen (rp, 1L); 
  274.     SetBPen (rp, 0L);
  275.     SetFont (rp, font);
  276.     RectFill (rp, 0L, 10L, WINDOWWIDTH-1L, WINDOWHEIGHT-1L);
  277.     SetDrMd (rp, JAM2 | INVERSVID);
  278.     SetWindowTitles (win, "YaBoing II", COPYRIGHT);
  279.  
  280.     if (vp->Modes & HIRES) xshiftfactor = 1; else xshiftfactor = 0;
  281.     if (vp->Modes & LACE)  yshiftfactor = 1; else yshiftfactor = 0;
  282.  
  283.     quartx = (halfx = (maxx = vp->DWidth >> xshiftfactor) >> 1) >> 1;
  284.     quarty = (halfy = (maxy = vp->DHeight >> yshiftfactor) >> 1) >> 1;
  285.     minx = -8; miny = -10; maxx += minx;  maxy += miny;
  286.     /* Minx, miny, maxx, maxy determine the box in which the sprites roam. */
  287. }
  288.  
  289. /* NewGame initializes everything necessary for a new game.
  290. */
  291. NewGame ()
  292. {
  293.     int cnt;
  294.     long creg; 
  295.  
  296.     for (cnt = 0; cnt < NUMSPR; cnt++) ybspr[cnt].mode = SPRITEDEAD;
  297.  
  298.     ClearStack ();
  299.     spritecount = 1;
  300. }
  301.        
  302.  
  303. /* CheckCollisions is called when a collision is detected. CheckCollisions
  304. ** checks to see who collided with whom and takes action accordingly...
  305. */
  306. CheckCollisions (clxdat)
  307. unsigned short clxdat;  /* Sprite collision register image */
  308. {
  309.     int cnt;
  310.  
  311.     /* First check collision of the two "number" sprites (sprites 2 and 4) */
  312.     if ((clxdat & COL2AND4) && 
  313.        (ybspr[0].mode==SPRITEALIVE) && (ybspr[1].mode==SPRITEALIVE))
  314.       ybspr[0].mode = ybspr[1].mode = SPRITEHIT1; /* Mark them as collided */
  315.  
  316.     /* Now check collisions between the mouse and the 3 sprites */
  317.     for (cnt = 0; cnt < NUMSPR; cnt++) 
  318.       if ((clxdat & colmasks[cnt]) && (ybspr[cnt].mode==SPRITEALIVE)) {
  319.         ybspr[cnt].mode = SPRITEHIT1;
  320.         ProcessHit (&ybspr[cnt]);
  321.       }
  322. }             
  323.  
  324.  
  325. /* Locate sprite will move a sprite to its new location if it's not dead. 
  326. */
  327. LocateSprite (spr)
  328. struct sprrec *spr;
  329. {
  330.   if (MODE != SPRITEDEAD) 
  331.     MoveSprite (NULL, &(spr->actualsprite), (long)PX, (long)PY);
  332. }
  333.  
  334. /* The "dissolvemasks" array determines how the sprites disappear when they die.
  335. */
  336. #define MAXDISSOLVEMASKS 7
  337. unsigned short dissolvemasks[MAXDISSOLVEMASKS] = {
  338.   0xeffb,0xeffb,0xef7b,0xcb6b,0xc94a,0x2108,0x0100
  339. };
  340.  
  341. ProcessSprite (spr)
  342. struct sprrec *spr;
  343. {
  344.     if (MODE != SPRITEDEAD) AdjustSprite (spr);
  345.  
  346.     switch (MODE) {
  347.      case SPRITEALIVE: ChangeNumValue (spr); break;
  348.      case SPRITEHIT1:  
  349.         VAL = AX = AY = 0;
  350.         VX >>= 1; VY >>= 1;
  351.         MODE = SPRITEHIT2; /* Fall through */
  352.      case SPRITEHIT2:
  353.         DissolveSprite (SPRMEM, dissolvemasks[VAL++]);
  354.         if (VAL == MAXDISSOLVEMASKS) MODE = SPRITEDEAD;
  355.     break;
  356.      case SPRITEDEAD:
  357.         ShowSprite (spr, false);
  358.         if (Rnd(5) == 0) EnterSprite (spr);
  359.         break;
  360.     }
  361. }
  362.  
  363. /* Given a sprite, this routine increases or decreases its value. For numbers,
  364. ** new value depends on the distance between the sprite and the mouse. For
  365. ** operator sprites, the value is incremented regularly.
  366. */
  367. ChangeNumValue (spr)
  368. struct sprrec *spr;
  369. {
  370.    int origval = VAL;
  371.    unsigned long newtc = TimeCount();
  372.  
  373.    if (newtc > CHANGE) {
  374.      if (TYPE == OPSPRITE) {
  375.        CHANGE = newtc + 14;   /* Change OPs every ~.9 seconds */
  376.        if (Rnd(40) == 0) VAL = OPVALUE+OPVALUES-1;
  377.        else if ((++VAL) >= OPVALUE+OPVALUES-1) VAL = OPVALUE;
  378.      } else {
  379.        CHANGE == newtc + 5;  /* And numbers 16/5 times a second */
  380.        if ((VAL > Rnd(8)) && (PX-mousex < quartx) && (PX-mousex > -quartx) && 
  381.            (PY-mousey < quarty) && (PY-mousey > -quarty)) VAL--;
  382.        else if (VAL < Rnd(10)) VAL += Rnd(3) - (VAL == DIGITVALUE ? 0 : 1);
  383.      }
  384.      if (origval != VAL) LoadSpriteImage (spr->sprmem, VAL);
  385.    }
  386. }
  387.  
  388.  
  389. /* This routine moves a sprite and adjusts its velocity and acceleration.
  390. ** It also checks to see if the sprite is out of bounds --- If it is, the
  391. ** sprite is made inactive.
  392. ** Delta is the change in time in 1/8 sec since last move.
  393. */
  394. AdjustSprite (spr)
  395. struct sprrec *spr;
  396. {
  397.     int delta = (TimeCount() - lastmove + 1);
  398.     if (delta > 32 || delta < 0) delta = 32;
  399.     
  400.     /* Below we update the sprite positions and change the velocities.
  401.     ** We make sure we remain within the speed limit (MAXVEL).
  402.     */
  403.     PX += (VX >> 1) * delta; /* Important that ">>" works */
  404.     PY += (VY >> 1) * delta; /* OK on signed quantities!  */  
  405.     VX += AX;  if (VX > MAXVEL || VX < -MAXVEL) {AX = 0; VX >>= 1;}; 
  406.     VY += AY;  if (VY > MAXVEL || VY < -MAXVEL) {AY = 0; VY >>= 1;}; 
  407.  
  408.     if (PX < minx || PX > maxx || PY < miny || PY > maxy) { 
  409.       MODE = SPRITEDEAD; ShowSprite (spr, false);  /* Out of bounds! Kill it! */
  410.     } else switch (Rnd(150)) {                     /* Randomly change stuff.  */
  411.       case 0: AX += (Rnd(5) - 2); AY += (Rnd(5) - 2); break;
  412.       case 1: VX = (VX > halfx ? -MAXVEL : MAXVEL); AX = AY = 0; break;
  413.       case 2: VX = -VX; break;
  414.       case 3: VY = -VY; break;
  415.       case 4: VX = -VX; AX = -AX; break;
  416.       case 5: VY = -VY; AY = -AY; break;
  417.       default: if (Rnd(7) == 0 && TYPE != OPSPRITE) { /* Move away from mouse */
  418.                 if (mousex > PX) AX = -Rnd(4); else AX = Rnd(4);
  419.                 if (mousey > PY) AY = -Rnd(4); else AY = Rnd(4);
  420.                }; break;
  421.     };
  422. }
  423.         
  424. /* Determines where a sprites comes into the screen from and sets the
  425. ** various parameters (velocity, acceleration, & position) accordingly...
  426. */
  427. EnterSprite (spr)
  428. struct sprrec *spr;
  429. {
  430.   int v = Rnd(5)+4;
  431.   int vo = Rnd(5)-2;
  432.   int a = (TYPE == OPSPRITE ? 0 : Rnd(3));
  433.  
  434.   switch (Rnd(4)) {
  435.     case 0: VX=v;  AX=a;  AY=0; VY=vo; PX=minx+1; PY=Rnd(halfy)+quarty; break;
  436.     case 1: VX=-v; AX=-a; AY=0; VY=vo; PX=maxx-1; PY=Rnd(halfy)+quarty; break;
  437.     case 2: VY=v;  AY=a;  AX=0; VX=vo; PY=miny+1; PX=Rnd(halfx)+quartx; break;
  438.     case 3: VY=-v; AY=-a; AX=0; VX=vo; PY=maxy-1; PX=Rnd(halfx)+quartx; break;
  439.   }
  440.  
  441.   if (TYPE == NUMSPRITE) VAL = Rnd(DIGITVALUES) + DIGITVALUE;
  442.   else VAL = Rnd(OPVALUES) + OPVALUE;  
  443.   
  444.   CHANGE = TimeCount();
  445.  
  446.   LoadSpriteImage (SPRMEM, VAL);
  447.   ShowSprite (spr, true);
  448.   MODE = SPRITEALIVE;  
  449.   
  450.   if (++spritecount == WARNSPRITE) SetWarnPointer (win, true);
  451. }
  452.     
  453.      
  454. /* ShowSprites disables/enables all sprites. Used when the user deactivates
  455. ** the YaBoing window.
  456. */
  457. ShowSprites (show)
  458. int show;
  459. {
  460.     int cnt;
  461.     for (cnt = 0; cnt < NUMSPR; cnt++)
  462.       if (show == false || ybspr[cnt].mode != SPRITEDEAD) 
  463.         ShowSprite (&ybspr[cnt], show);
  464. }
  465.  
  466.  
  467. /* InitSprites attempts to obtain the sprites we need. If unsuccessful, gives
  468. ** up in shame.
  469. */
  470. int InitSprites ()
  471. {
  472.     int cnt;
  473.     for (cnt = 0; cnt < NUMSPR; cnt++) {
  474.       ybspr[cnt].actualsprite.height = 0;
  475.       ybspr[cnt].type = (cnt == NUMSPR-1 ? OPSPRITE : NUMSPRITE);
  476.       if (InitSprite(&ybspr[cnt],cnt+cnt+2) == false) return (false);
  477.     };
  478.     return (true);
  479. }
  480.  
  481.   
  482. static unsigned long rndseed;
  483.  
  484. InitRnd ()
  485. {
  486.   rndseed = TimeCount();
  487. }
  488.  
  489.  
  490. /* Returns random integer between 0 and max-1 inclusive. 
  491. */
  492. int Rnd (max)
  493. int max;
  494. {
  495.   long res = (rndseed & 0x00000002L) | (rndseed & 0x00000010L);
  496.   rndseed >>= 1;
  497.   if (res == 0x00000012 || res == 0x00000000) rndseed |= 0x80000000L;
  498.   return (((int)((rndseed & 0x00007fffL) % max)));
  499. }
  500.  
  501.  
  502. /* YaBoing The Next Generation, by Ali T. Ozer */
  503.  
  504.  
  505.  
  506.